/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_dbaccess.hxx"
#ifndef DBAUI_TABLEWINDOWLISTBOX_HXX
#include "TableWindowListBox.hxx"
#endif
#ifndef DBAUI_TABLEWINDOW_HXX
#include "TableWindow.hxx"
#endif
#ifndef DBAUI_QUERYDESIGNVIEW_HXX
#include "QueryDesignView.hxx"
#endif
#ifndef DBAUI_QUERYTABLEVIEW_HXX
#include "QueryTableView.hxx"
#endif
#ifndef DBAUI_QUERYCONTROLLER_HXX
#include "querycontroller.hxx"
#endif
#ifndef DBAUI_JOINEXCHANGE_HXX
#include "JoinExchange.hxx"
#endif
#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif
#ifndef _COM_SUN_STAR_SDBC_XDATABASEMETADATA_HPP_
#include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
#endif
#ifndef _SVX_DBEXCH_HRC
#include <svx/dbexch.hrc>
#endif
#ifndef _SV_SVAPP_HXX 
#include <vcl/svapp.hxx>
#endif

using namespace dbaui;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::datatransfer;

OJoinExchangeData::OJoinExchangeData(OTableWindowListBox* pBox) 
	: pListBox(pBox)
	, pEntry(pBox->FirstSelected()) 
{ }

const ULONG SCROLLING_TIMESPAN = 500;
const long LISTBOX_SCROLLING_AREA = 6;
//==================================================================
// class OTableWindowListBox
//==================================================================
DBG_NAME(OTableWindowListBox)
//------------------------------------------------------------------------------
OTableWindowListBox::OTableWindowListBox( OTableWindow* pParent )
    :SvTreeListBox( pParent, WB_HASBUTTONS | WB_BORDER)
	,m_aMousePos( Point(0,0) )
	,m_pTabWin( pParent )
	,m_nDropEvent(0)
	,m_nUiEvent(0)
	,m_bReallyScrolled( sal_False )
{
	DBG_CTOR(OTableWindowListBox,NULL);
	m_aScrollTimer.SetTimeout( SCROLLING_TIMESPAN );
	SetDoubleClickHdl( LINK(this, OTableWindowListBox, OnDoubleClick) );

	SetSelectionMode(SINGLE_SELECTION);

	SetHighlightRange( );
}

//------------------------------------------------------------------------------
void OTableWindowListBox::dragFinished( )
{
	// first show the error msg when existing
	m_pTabWin->getDesignView()->getController().showError(m_pTabWin->getDesignView()->getController().clearOccuredError());
	// second look for ui activities which should happen after d&d
	if (m_nUiEvent)
		Application::RemoveUserEvent(m_nUiEvent);
	m_nUiEvent = Application::PostUserEvent(LINK(this, OTableWindowListBox, LookForUiHdl));
}

//------------------------------------------------------------------------------
OTableWindowListBox::~OTableWindowListBox()
{
	DBG_DTOR(OTableWindowListBox,NULL);
	if (m_nDropEvent)
		Application::RemoveUserEvent(m_nDropEvent);
	if (m_nUiEvent)
		Application::RemoveUserEvent(m_nUiEvent);
	if( m_aScrollTimer.IsActive() )
		m_aScrollTimer.Stop();
	m_pTabWin = NULL;
}

//------------------------------------------------------------------------------
SvLBoxEntry* OTableWindowListBox::GetEntryFromText( const String& rEntryText )
{
	//////////////////////////////////////////////////////////////////////
	// Liste durchiterieren
	SvTreeList* pTreeList = GetModel();
	SvLBoxEntry* pEntry = (SvLBoxEntry*)pTreeList->First();
	OJoinDesignView* pView = m_pTabWin->getDesignView();
	OJoinController& rController = pView->getController();

	BOOL bCase = FALSE;
	try
	{
		Reference<XConnection> xConnection = rController.getConnection();
		if(xConnection.is())
		{
			Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
			if(xMeta.is())
				bCase = xMeta->supportsMixedCaseQuotedIdentifiers();
		}
		while( pEntry )
		{
			if((bCase ? rEntryText == GetEntryText(pEntry) : rEntryText.EqualsIgnoreCaseAscii(GetEntryText(pEntry))))
			{
				return pEntry;
			}
			pEntry = (SvLBoxEntry*)pTreeList->Next( pEntry );
		}
	}
	catch(SQLException&)
	{
	}

	return NULL;
}

//------------------------------------------------------------------------------
void OTableWindowListBox::NotifyScrolled()
{
	m_bReallyScrolled = TRUE;
}

//------------------------------------------------------------------------------
void OTableWindowListBox::NotifyEndScroll()
{
	if (m_bReallyScrolled)
		// die Verbindungen, die diese Tabelle eventuell hat, muessen neu gezeichnet werden
		m_pTabWin->getTableView()->Invalidate(INVALIDATE_NOCHILDREN);
		// ohne das INVALIDATE_NOCHILDREN wuerden auch alle Tabellen neu gezeichnet werden,
		// sprich : es flackert
	m_bReallyScrolled = FALSE;
}

//------------------------------------------------------------------------------
long OTableWindowListBox::PreNotify(NotifyEvent& rNEvt)
{
	BOOL bHandled = FALSE;
	switch (rNEvt.GetType())
	{
		case EVENT_KEYINPUT:
		{
			const KeyEvent* pKeyEvent =	rNEvt.GetKeyEvent();
			const KeyCode& rCode = pKeyEvent->GetKeyCode();
			
			if (rCode.GetCode() != KEY_RETURN)
			{
				if(m_pTabWin)
				{
					bHandled = m_pTabWin->HandleKeyInput(*pKeyEvent);
					//	bHandled = TRUE;
				}
				break;
			}

			if (rCode.IsMod1() || rCode.IsMod2() || rCode.IsShift())
				break;
			if (FirstSelected())
				static_cast<OTableWindow*>(Window::GetParent())->OnEntryDoubleClicked(FirstSelected());
		}
		break;
	}

	if (!bHandled)
		return SvTreeListBox::PreNotify(rNEvt);
	return 1L;
}

//------------------------------------------------------------------------------
IMPL_LINK( OTableWindowListBox, ScrollUpHdl, SvTreeListBox*, /*pBox*/ )
{
	SvLBoxEntry* pEntry = GetEntry( m_aMousePos );
	if( !pEntry )
		return 0;

	if( pEntry != Last() )
	{
		ScrollOutputArea( -1 );
		pEntry = GetEntry( m_aMousePos );
		Select( pEntry, TRUE );
//		m_aScrollTimer.Start();
	}

	return 0;
}

//------------------------------------------------------------------------------
IMPL_LINK( OTableWindowListBox, ScrollDownHdl, SvTreeListBox*, /*pBox*/ )
{
	SvLBoxEntry* pEntry = GetEntry( m_aMousePos );
	if( !pEntry )
		return 0;

	if( pEntry != Last() )
	{
		ScrollOutputArea( 1 );
		pEntry = GetEntry( m_aMousePos );
		Select( pEntry, TRUE );
//		m_aScrollTimer.Start();
	}

	return 0;
}

//------------------------------------------------------------------------------
void OTableWindowListBox::StartDrag( sal_Int8 /*nAction*/, const Point& /*rPosPixel*/ )
{
	OJoinTableView* pCont = m_pTabWin->getTableView();
	if (!pCont->getDesignView()->getController().isReadOnly() && pCont->getDesignView()->getController().isConnected())
	{
		// #100271# OJ asterix was not allowed to be copied to selection browsebox
		sal_Bool bFirstNotAllowed = FirstSelected() == First() && m_pTabWin->GetData()->IsShowAll();
		EndSelection();
		// create a description of the source
		OJoinExchangeData jxdSource(this);
		// put it into a exchange object
		OJoinExchObj* pJoin = new OJoinExchObj(jxdSource,bFirstNotAllowed);
		Reference< XTransferable > xEnsureDelete(pJoin);
		pJoin->StartDrag(this, DND_ACTION_LINK, this);
	}
}

//------------------------------------------------------------------------------
sal_Int8 OTableWindowListBox::AcceptDrop( const AcceptDropEvent& _rEvt )
{
	sal_Int8 nDND_Action = DND_ACTION_NONE;
	// check the format
	if ( !OJoinExchObj::isFormatAvailable(GetDataFlavorExVector(),SOT_FORMATSTR_ID_SBA_TABID) // this means that the first entry is to be draged
		&& OJoinExchObj::isFormatAvailable(GetDataFlavorExVector(),SOT_FORMATSTR_ID_SBA_JOIN) )
	{	// don't drop into the window if it's the drag source itself

		
		// remove the selection if the dragging operation is leaving the window
		if (_rEvt.mbLeaving)
			SelectAll(FALSE);
		else
		{
			// hit test
			m_aMousePos = _rEvt.maPosPixel;
			Size aOutputSize = GetOutputSizePixel();
			SvLBoxEntry* pEntry = GetEntry( m_aMousePos );
			if( !pEntry )
				return DND_ACTION_NONE;

			// Scrolling Areas
			Rectangle aBottomScrollArea( Point(0, aOutputSize.Height()-LISTBOX_SCROLLING_AREA),
										 Size(aOutputSize.Width(), LISTBOX_SCROLLING_AREA) );
			Rectangle aTopScrollArea( Point(0,0), Size(aOutputSize.Width(), LISTBOX_SCROLLING_AREA) );

			// Wenn Zeiger auf der oberen ScrollingArea steht, nach oben scrollen
			if( aBottomScrollArea.IsInside(m_aMousePos) )
			{
				if( !m_aScrollTimer.IsActive() )
				{
					m_aScrollTimer.SetTimeoutHdl( LINK(this, OTableWindowListBox, ScrollUpHdl) );
					ScrollUpHdl( this );
				}
			}

			// Wenn Zeiger auf der oberen ScrollingArea steht, nach unten scrollen
			else if( aTopScrollArea.IsInside(m_aMousePos) )
			{
				if( !m_aScrollTimer.IsActive() )
				{
					m_aScrollTimer.SetTimeoutHdl( LINK(this, OTableWindowListBox, ScrollDownHdl) );
					ScrollDownHdl( this );
				}
			}
			else
			{
				if( m_aScrollTimer.IsActive() )
					m_aScrollTimer.Stop();
			}

			// Beim Drag automatisch den richtigen Eintrag selektieren
			if ((FirstSelected() != pEntry) || (FirstSelected() && NextSelected(FirstSelected())))
				SelectAll(FALSE);
			Select(pEntry, TRUE);

			// Auf den ersten Eintrag (*) kann nicht gedroppt werden
			if(!( m_pTabWin->GetData()->IsShowAll() && (pEntry==First()) ))
				nDND_Action = DND_ACTION_LINK;
		}
	}
	return nDND_Action;
}
// -----------------------------------------------------------------------------

//------------------------------------------------------------------------------
IMPL_LINK( OTableWindowListBox, LookForUiHdl, void *, /*EMPTY_ARG*/)
{
	m_nUiEvent = 0;
	m_pTabWin->getTableView()->lookForUiActivities();
	return 0L;
}
//------------------------------------------------------------------------------
IMPL_LINK( OTableWindowListBox, DropHdl, void *, /*EMPTY_ARG*/)
{
	// create the connection
	m_nDropEvent = 0;
	OSL_ENSURE(m_pTabWin,"No TableWindow!");
	try
	{
		OJoinTableView* pCont = m_pTabWin->getTableView();
		OSL_ENSURE(pCont,"No QueryTableView!");
		pCont->AddConnection(m_aDropInfo.aSource, m_aDropInfo.aDest);
	}
	catch(const SQLException& e)
	{
		// remember the exception so that we can show them later when d&d is finished
		m_pTabWin->getDesignView()->getController().setErrorOccured(::dbtools::SQLExceptionInfo(e));
	}
	return 0L;
}
//------------------------------------------------------------------------------
sal_Int8 OTableWindowListBox::ExecuteDrop( const ExecuteDropEvent& _rEvt )
{
	TransferableDataHelper aDropped(_rEvt.maDropEvent.Transferable);
	if ( OJoinExchObj::isFormatAvailable(aDropped.GetDataFlavorExVector()))
	{	// don't drop into the window if it's the drag source itself
		m_aDropInfo.aSource	= OJoinExchangeData(this);
		m_aDropInfo.aDest	= OJoinExchObj::GetSourceDescription(_rEvt.maDropEvent.Transferable);

		if (m_nDropEvent)
			Application::RemoveUserEvent(m_nDropEvent);
		m_nDropEvent = Application::PostUserEvent(LINK(this, OTableWindowListBox, DropHdl));

		return DND_ACTION_LINK;
	}
	return DND_ACTION_NONE;
}

//------------------------------------------------------------------------------
void OTableWindowListBox::LoseFocus()
{
	if(m_pTabWin)
		m_pTabWin->setActive(sal_False);
	SvTreeListBox::LoseFocus();
}

//------------------------------------------------------------------------------
void OTableWindowListBox::GetFocus()
{
	if(m_pTabWin)
		m_pTabWin->setActive();

	if (GetCurEntry() != NULL)
	{
		if ( GetSelectionCount() == 0 || GetCurEntry() != FirstSelected() )
		{
			if ( FirstSelected() )
				Select(FirstSelected(), FALSE);
			Select(GetCurEntry(), TRUE);
		}
		else
			ShowFocusRect(FirstSelected());
	}
	SvTreeListBox::GetFocus();	
}

//------------------------------------------------------------------------------
IMPL_LINK( OTableWindowListBox, OnDoubleClick, SvTreeListBox *, /*pBox*/ )
{
	// meinem Elter Bescheid sagen
	Window* pParent = Window::GetParent();
	DBG_ASSERT(pParent != NULL, "OTableWindowListBox::OnDoubleClick : habe kein Parent !");

	static_cast<OTableWindow*>(pParent)->OnEntryDoubleClicked(GetHdlEntry());

	return 0;
}
// -----------------------------------------------------------------------------
void OTableWindowListBox::Command(const CommandEvent& rEvt)
{
	switch (rEvt.GetCommand())
	{
		case COMMAND_CONTEXTMENU:
		{
			static_cast<OTableWindow*>(Window::GetParent())->Command(rEvt);
			break;
		}
		default:
			SvTreeListBox::Command(rEvt);
	}
}
// -----------------------------------------------------------------------------
